<?php
namespace App\Core;


abstract class View
{
    const HTML_ID_PREFIX = 'app_id_';

    /**
     * 父组件
     * @var View
     */
    private $_parent;


    /**
     * 组件id
     * @var int
     */
    private $_htmlId;

    /**
     * 参数槽
     * @var array
     */
    private $params;

    /**
     * 组件构造函数，每个组件都有父组件，最早的祖先组件是一个页面类
     *
     * @param View $parent 父组件对象
     * @param int $html_id 组件id
     */
    public function __construct($parent = NULL, $html_id = NULL)
    {
        $this->_parent = $parent;
        $this->_htmlId = $html_id;
    }


    /**
     * 获取父组件
     * @return View
     */
    public function getParent()
    {
        return $this->_parent;
    }

    /**
     * 获取组件id
     * @return string
     */
    public function get_html_id()
    {
        return $this->_htmlId;
    }


    /**
     * 注册javascript路径，每个路径对应单独的js文件
     *
     * @return array
     */
    public static function useJavaScripts()
    {
        return [];
    }

    /**
     * 注册css路径，每个路径对应单独的css文件
     */
    public static function useStyles()
    {
        return [];
    }

    /**
     * 注册javascript路径，所有js文件将合并到名为页面类的js中。
     */
    public static function useBoundableJavaScripts()
    {
        return [];
    }

    /**
     * 注册css路径，所有css文件将合并到名为页面类的css中。
     */
    public static function useBoundableStyles()
    {
        return [];
    }

    /**
     * 注册子组件
     * @return array
     */
    public static function useComponent()
    {
        return [];
    }


    public static function useInlineStyles()
    {
        return false;
    }

    public static function prefetchJavaScripts()
    {
        return [];
    }

    public static function prefetchStyles()
    {
        return [];
    }

    public static function prefetchImages()
    {
        return array();
    }

    public static function useInlineScripts()
    {
        return [];
    }


    public function setParams($params = array())
    {
        $this->params = $params;
    }

    /**
     * 获取参数
     * @return array
     */
    public function getParams()
    {
        return $this->params;
    }

    /**
     * 获取指定参数
     * @param string $name
     */
    public function getParam($name)
    {
        return $this->params[$name];
    }


    /**
     * 设置页面变量
     *
     * @param string $name 变量名
     * @param mixed $value
     */

    public function assignData($name, $value)
    {
        $this->data[$name] = $value;
    }

    /**
     * 获取所有页面变量
     */
    public function getData()
    {
        return $this->data;
    }

    /**
     * 变量槽
     *
     * @var array
     */
    private $data = [];

    /**
     * 载入组件显示页面
     */
    abstract public function execute();


    /**
     * 类名转换到文件路径
     *
     * @param string $className
     * @param string $explode
     * @return string
     */
    protected function classNameToPath($className, $explode = '\\')
    {
        return implode(DIRECTORY_SEPARATOR, explode($explode, $className));
    }

    /**
     * 开始输出脚本
     */
    public function scriptBlockBegin()
    {
        ob_start();
    }

    /**
     * 结束脚本输出
     *
     * @param int $order
     */
    public function scriptBlockEnd($order = 0)
    {
        $content = ob_get_contents();
        $this->registerScriptBlock($content, $order);
        ob_end_clean();
    }

    private $script_blocks = [];
    private $resource_index = 0;
    private $script_blocks_processed = false;

    public function registerScriptBlock($content, $order = 0)
    {
        $this->script_blocks[] = [
            'content' => $content,
            'order' => $order,
            'index' => $this->resource_index++
        ];
    }

    public function getScriptBlocks()
    {
        if (!$this->script_blocks_processed) {
            $values = $this->script_blocks;
            usort($values, [$this, 'resourceOrderComparator']);
            $this->script_blocks = [];
            foreach ($values as $value) {
                $this->script_blocks[] = $value['content'];
            }
            $this->script_blocks_processed = true;
        }
        return $this->script_blocks;
    }

    /**
     * 给资源排序 usort 回调函数
     *
     * @param array $a
     * @param array $b
     * @return int
     */
    public function resourceOrderComparator($a, $b)
    {
        if ($a['order'] == $b['order']) {
            if ($a['index'] == $b['index']) {
                return 0;
            }
            return ($a['index'] > $b['index']) ? 1 : -1;
        }
        return ($a['order'] > $b['order']) ? -1 : 1;
    }

    /**
     * 加载页面框架:
     * getView，获取页面phtml文件名，
     * 然后渲染此phtml文件
     * @see Component::execute()
     */
    abstract public function getView();

    /**
     * 渲染页面，也就是载入文件
     * 设置所有页面变量，保证phtml的php脚本可以引用
     *
     * @param string $file 文件路径
     */
    protected function render($file)
    {
        foreach ($this->getParams() as $key => $value) {
            $$key = $value;
        }

        extract($this->params);
        extract($this->data);

        include($file);
    }

    /**
     * 渲染 Component
     *
     * @param string $componentName
     * @param array $params
     * @return View
     */
    public function component($componentName, $params = array())
    {
        $componentClass = $componentName . 'Component';
        if (!$componentClass) {
            return false;
        }
        $this->_htmlId++;
        /**
         * @var View $component
         */
        $component = new $componentClass($this, self::HTML_ID_PREFIX . $this->_htmlId);

        $component->setParams($params);
        $component->execute();
        return $component;
    }

    /**
     * 获取组件的页面类
     *
     * @return View
     */
    protected function getPage()
    {
        $object = $this->getParent();
        while ($object) {
            if ($object instanceof View) {
                return $object;
            }
            $object = $object->getParent();
        }
        return null;
    }
}